require 'generator/generator'
require 'parser/variable'

class Function
  attr_accessor :scope, :name, :return_var, :arguments
  def initialize(scope, name, return_var, arguments)
    @scope = scope
    @name = name
    @return_var = return_var
    @arguments = arguments
    @return_index = 0
  end
  
  def next_return_index
    @return_index = @return_index + 1
    return @return_index
  end
  
  def header
    Generator.write_line
    Generator.raw_line("/* Start function #{@name} */")
    Generator.write_line("goto SCOPE#{@scope.name}END_#{@name};", "Jump past the declaration")
    Generator.raw_line("SCOPE#{@scope.name}_#{@name}:")
    Generator.push_frame()
    Generator.write_line("", "v--Begin Function Parameters--v")
    @arguments.each do |arg|
      arg.allocate
    end
    Generator.write_line("", "^-- End Function Parameters --^")
    @return_var.allocate
  end
  
  def footer
    @return_var.load(:RV)
    Generator.pop_frame()
    Generator.raw_line("SCOPE#{@scope.name}END_#{@name}:")
  end
  
  def pass_argument(index, arg)
    (var, index_reg) = arg
    t1 = var.var_type
    if(index >= arguments.length)
      raise "Incorrect number of arguments for function '#{name}' (requires #{arguments.length})"
    end
    t2 = arguments[index].var_type
    # Type-checking
    unless(t1 == t2)
      raise "Type mismatch for function #{name} argument #{index + 1}. Expected #{t2}, found #{t1}."
    end
    if(index_reg.nil?)
      var.load_address(:ACC)
    else
      var.load_address(:ACC, true, index_reg)
    end
    inst = "MEM[Reg[SP] + #{3+index}] = Reg[ACC];"
    comment = "Pass argument #{index + 1} to #{@name}"
    Generator.write_line(inst, comment)
  end
  
  def call
    # puts "Called call:#{@name}"
    return_label = "SCOPE#{@scope.name}_RET#{next_return_index}_#{@name}"
    inst = "RA = &&#{return_label};"
    comment = "Store the return address to get back here"
    Generator.write_line(inst, comment)
    inst = "MEM[Reg[SP] + 2] = (int)RA;"
    comment = "Push the return address into the stack frame"
    Generator.write_line(inst, comment)
    inst = "goto SCOPE#{@scope.name}_#{@name};"
    args = Array.new
    @arguments.each do |arg|
      args << arg.var_type
    end
    comment = "Call #{@return_var.var_type} #{@name}(#{args.join(', ')})"
    Generator.write_line(inst, comment)
    Generator.raw_line("#{return_label}:")
  end
end